/* 
* arp_lib.c
* 
* Copyright Anthony Lineberry 2007 
* 
* This file is copyright and licensed under the GNU GPL v2.0 
* 
*/ 
#include "eth.h"
#include "pci.h"
#include "adapt.h"
#include "ne2k.h"
#include "pack.h"
#include "arp.h"

// arp cache
static arpent arpCache[256];
int arpIndex = 0;

// arp reply wait system
static arpreq_wait waitCache[256];
int waitIndex = 0;

// displays the contents of the arp cache
void arpDisplayCache()
{
	// display it
	printf( "ARP cache:\n" );
	int i;
	for( i = 0; i < 256; i++ )
	{
		if( arpCache[i].avail )
		{
			printf( "%d.%d.%d.%d\t\t%x:%x:%x:%x:%x:%x\n", IPAddr0( arpCache[i].IP ), IPAddr1( arpCache[i].IP ), IPAddr2( arpCache[i].IP ), IPAddr3( arpCache[i].IP ),
															arpCache[i].ethAddr[0], arpCache[i].ethAddr[1], arpCache[i].ethAddr[2], arpCache[i].ethAddr[3], arpCache[i].ethAddr[4], arpCache[i].ethAddr[5] );
		}
	}
}

// displays wait info
void arpDisplayWaitInfo()
{
	//
}

// intializes waiting
void InitArpReplyWait()
{
	// clear all waits
	int i;
	for( i = 0; i < 256; i++ )
	{
		waitCache[i].free = 1;
		waitCache[i].code = i;
		waitCache[i].destip = 0;
		waitCache[i].waiting = 0;
	}
}

// clear the cache
void ClearCache()
{	
	// clear the cache
	int i;
	for( i = 0; i < 256; i++ )
	{
		// not available
		arpCache[i].avail = 0;
		
		// null ip
		arpCache[i].IP = 0;
		
		// null MAC
		memset( arpCache[i].ethAddr, 0x00, 6 );
	}
	
	// set the index to 0
	arpIndex = 0;
}

// checks the arp cache for an address
int checkArpCache( unsigned int myIp, arpent* myEnt )
{
	// search...
	int i;
	for( i = 0; i < 256; i++ )
	{
		if( arpCache[i].avail == 1 )
		{
			if( arpCache[i].IP == myIp )
			{
				*myEnt = arpCache[i];
				return 1;
			}
		}
	}

	return 0;
}

// adds an entry to the cache
void addEntToCache( arpent myEnt )
{
	// add it
	arpCache[arpIndex].avail = 1;
	arpCache[arpIndex].IP = myEnt.IP;
	memcpy( arpCache[arpIndex].ethAddr, myEnt.ethAddr, 6 );
	arpIndex++;
}

// waits for an arp reply
int waitForArpReply( int code )
{
int sec =0;
	// wait for it (20s max)
	printf("Waiting ARP.... \n");
	while( waitCache[code].waiting )
	{
		
		printf("#");
		timer_wait(1);
		sec++;
		if(sec > 20)
			return -1;
	}
	return 0;
}

// signals an arp reply
void signalArpReply( int srcip )
{			
	// find this ip in the code table
	int i;
	for( i = 0; i < 256; i++ )
	{
		if( waitCache[i].destip == srcip )
		{
			// good to go
			waitCache[i].waiting = 0;
			waitCache[i].free = 1;
			
			// stop searching (means stuff happens chronologically)
			return;
		}
	}
}

// returns the code to use
int SendArpRequest( unsigned int destIp )
{
	// create an ethernet packet, send it off
	packet_t* myPacket;
	struct ethHeader_t* ethPkt;
	struct arpPacket_t* arp;
	
	// create the packet
	char* data = (char*) kmalloc( sizeof( struct ethHeader_t ) + sizeof( struct arpPacket_t ) );
	myPacket = packetCreate( data, sizeof( struct ethHeader_t ) + sizeof( struct arpPacket_t ) );
	
	// setup headers
	ethPkt = (struct ethHeader_t*) myPacket->data;
	arp = (struct arpPacket_t*) ethernetStripHeader( myPacket );
	
	// get the card
	struct ne2000_t* myCard = (struct ne2000_t*) getNeCard( 0 );
	
	// fill in the data
	memcpy( ethPkt->dest, "\xff\xff\xff\xff\xff\xff", 6 );
	memcpy( ethPkt->src, myCard->eth->macAddr, 6 );
	ethPkt->type = htons( 0x0806 ); // arp
	arp->hwType = htons( 1 ); // ethernet
	arp->protocol = htons( 0x0800 ); // ipv4
	arp->hwSize = 6; // 6 bits in MAC
	arp->protocolSize = 4; // 4 bits in ip
	arp->opcode = htons( ARP_OP_REQUEST );
	memcpy( arp->hwDest, "\xff\xff\xff\xff\xff\xff", 6 );
	arp->ipDest[0] = IPAddr0( destIp );
	arp->ipDest[1] = IPAddr1( destIp );
	arp->ipDest[2] = IPAddr2( destIp );
	arp->ipDest[3] = IPAddr3( destIp );
	memcpy( arp->hwSrc, myCard->eth->macAddr, 6 );
	*((uint32_t*) arp->ipSrc) = myCard->eth->adapter->ipaddr;

	printf("- dit zie je wel \n");	
	// send it
	myCard->eth->send( myCard->eth, myPacket );
	printf("- dit zie je niet \n");
	// get the next free wait id, fill it
	int i;
	for( i = 0; i < 256; i++ )
	{
		if( waitCache[i].free == 1 )
		{
			waitCache[i].destip = destIp;
			waitCache[i].free = 0;
			waitCache[i].waiting = 1;
			return waitCache[i].code;
		}
	}
	return -1;
}

// finds a client by sending an ARP request
// will wait for the arp reply to come (timeout of 10 seconds)
int findClient( unsigned int myIp, arpent* myEnt )
{
	// send it
	int theCode = SendArpRequest( myIp );
	
	// wait for the reply
	int waitCode = waitForArpReply( theCode );
	
	// couldn't get a reply
	if( waitCode == -1 )
		return -1;
	
	// get the data
	int i;
	for( i = 0; i < 256; i++ )
	{
		if( arpCache[i].avail == 1 )
		{
			if( arpCache[i].IP == myIp )
			{
				*myEnt = arpCache[i];
				break;
			}
		}
	}

	// return to caller, we have found it
	return 0;
}

void handleArp(struct ethernet_t *eth, packet_t *pkt)
{
	struct ethHeader_t *ethPkt;
	struct arpPacket_t *arp;
	netAdapter_t *nic;

	if(!eth || !eth->adapter)
		return;
	nic = eth->adapter;
	
	if(!pkt || !pkt->data)
		return;

	ethPkt = (struct ethHeader_t *)pkt->data;
	arp = (struct arpPacket_t *)ethernetStripHeader(pkt);
	
	if(ntohs(arp->protocol) == ETHER_FRAME_IP)
	{
		if(*(uint32_t *)arp->ipDest == nic->ipaddr)
		{
			if(arp->opcode == ntohs(ARP_OP_REQUEST))
			{
				// add to the cache
				arpent myEnt;
				myEnt.IP = inetAddress( arp->ipSrc[0], arp->ipSrc[1], arp->ipSrc[2], arp->ipSrc[3] );
				memcpy( myEnt.ethAddr, arp->hwSrc, 6 );
				addEntToCache( myEnt );

				// create the return packet
				memcpy(ethPkt->dest, ethPkt->src, 6);
				memcpy(ethPkt->src, eth->macAddr, 6);
				arp->opcode = htons(ARP_OP_REPLY);
				memcpy(arp->hwDest, arp->hwSrc, 6);
				memcpy(arp->ipDest, arp->ipSrc, 4);
				memcpy(arp->hwSrc, eth->macAddr, 6);
				*((uint32_t *)arp->ipSrc) = nic->ipaddr;
				eth->send(eth, pkt);
			}
			else if(arp->opcode == ntohs(ARP_OP_REPLY))
			{
				// fill it in
				arpent myEnt;
				myEnt.IP = inetAddress( arp->ipSrc[0], arp->ipSrc[1], arp->ipSrc[2], arp->ipSrc[3] );
				memcpy( myEnt.ethAddr, arp->hwSrc, 6 );
				addEntToCache( myEnt );
				
				// signal it
				signalArpReply( myEnt.IP );
			}
		}
		else
		{
			printf("got ARP but not for us...\n");
			// not for us
		}
	}
}
